/*
 * $Id: stdlib_rc4.c,v 1.6 2006-03-11 12:33:37 bacon Exp $
 */

#include <xp/bas/stdlib.h>
#include <xp/bas/memory.h>

xp_rc4_t* xp_rc4_open (xp_rc4_t* rc4)
{
	if (rc4 == XP_NULL) {
		rc4 = (xp_rc4_t*)xp_malloc (xp_sizeof(xp_rc4_t));
		if (rc4 == XP_NULL) return XP_NULL;
		rc4->__dynamic = xp_true;
	}
	else rc4->__dynamic = xp_false;

	rc4->passwd_len = 0;
	xp_rc4_restart (rc4);

	return rc4;	
}

void xp_rc4_close (xp_rc4_t* rc4)
{
	/* no clean-up necessary */
	if (rc4->__dynamic) xp_free (rc4);
}

void xp_rc4_set_passwd (xp_rc4_t* rc4, const void* pw, xp_size_t psz)
{
	rc4->passwd_len = 
		(psz > xp_sizeof(rc4->passwd))? xp_sizeof(rc4->passwd): psz;
	xp_memcpy (rc4->passwd, pw, rc4->passwd_len);
	xp_rc4_restart (rc4);
}

void xp_rc4_restart (xp_rc4_t* rc4)
{
	int i, j, t;

	if (rc4->passwd_len == 0) return;

	rc4->idxa = 0;
	rc4->idxb = 0;

	for (i = 0; i <= 255; i++) {
		rc4->key[i] = rc4->passwd[i % rc4->passwd_len];
		rc4->sbox[i] = i;
	}
	for (i = 0, j = 0; i <= 255; i++) {
		j = (j + rc4->sbox[i] + rc4->key[i]) % 256;

		t = rc4->sbox[i];
		rc4->sbox[i] = rc4->sbox[j];
		rc4->sbox[j] = t;
	}
}

xp_byte_t xp_rc4_cipher (xp_rc4_t* rc4, xp_byte_t c)
{
	int t;

	/* when the password is not set, it just
	 * return the value given as it is
	 */
	if (rc4->passwd_len == 0) return c;

	rc4->idxa = (rc4->idxa + 1) % 256;
	rc4->idxb = (rc4->idxb + rc4->sbox[rc4->idxa]) % 256;

	t = rc4->sbox[rc4->idxa];
	rc4->sbox[rc4->idxa] = rc4->sbox[rc4->idxb];
	rc4->sbox[rc4->idxb] = t;

	return c ^ (rc4->sbox[(rc4->sbox[rc4->idxa] + rc4->sbox[rc4->idxb]) % 256]);
}

xp_size_t xp_cipherrc4 (
	const void* src, xp_size_t ssz,
	void* dst, xp_size_t dsz, 
	const void* pw, xp_size_t pwlen)
{
	xp_byte_t* d = (xp_byte_t*)dst;
	const xp_byte_t* s = (const xp_byte_t*)src;
	const xp_byte_t* p = (const xp_byte_t*)pw;

	int sbox[256], key[256];
	int j, a, b, t;
	xp_size_t i, sz;

	if (pwlen == 0) {
		xp_size_t len = (dsz > ssz)? ssz: dsz;
		xp_memcpy (dst, src, (dsz > ssz)? ssz: dsz);
		return len;
	}

	for (i = 0; i <= 255; i++) { 
		key[i] = p[i % pwlen];
		sbox[i] = (int)i;
	}

	for (i = 0, j = 0; i <= 255; i++) {
		j = (j + sbox[i] + key[i]) % 256;

		t = sbox[i];
		sbox[i] = sbox[j];
		sbox[j] = t;
	}

	a = b = 0;
	sz = (ssz >= dsz)? dsz: ssz;
	for (i = 0; i < ssz; i++) {
		a = (a + 1) % 256;
		b = (b + sbox[a]) % 256;

		t = sbox[a];
		sbox[a] = sbox[b];
		sbox[b] = t;

		d[i] = s[i] ^ (sbox[(sbox[a] + sbox[b]) % 256]);
	}	

	return sz;
}
